#include "threadsafe_queue.h"

template <typename T>
threadsafe_queue<T>::threadsafe_queue(const threadsafe_queue &rhs)
{
    std::lock_guard lg(rhs._mutex);
    _queue = rhs._queue;
}

template <typename T>
bool threadsafe_queue<T>::empty() const
{
    std::lock_guard lg(_mutex);
    return _queue.empty();
}

template <typename T>
void threadsafe_queue<T>::push(T new_value)
{
    std::lock_guard lg(_mutex);
    _queue.push(new_value);
    _cond.notify_one();
}

template <typename T>
void threadsafe_queue<T>::wait_and_pop(T &value)
{
    std::unique_lock ul(_mutex);
    _cond.wait(ul, [this]{ return !_queue.empty(); });
    value = _queue.front();
    _queue.pop();
}

template <typename T>
shared_ptr<T> threadsafe_queue<T>::wait_and_pop()
{
    std::unique_lock ul(_mutex);
    _cond.wait(ul, [this]{ return !_queue.empty(); });
    shared_ptr<T> res(std::make_shared<T>(_queue.front()));
    _queue.pop();
    return res;
}

template <typename T>
bool threadsafe_queue<T>::try_pop(T &value)
{
    std::lock_guard lg(_mutex);
    if(_queue.empty()) return false;
    value = _queue.front();
    _queue.pop();
    return true;
}

template <typename T>
shared_ptr<T> threadsafe_queue<T>::try_pop()
{
    std::lock_guard lg(_mutex);
    if (_queue.empty())
        return shared_ptr<T>();
    shared_ptr<T> res(std::make_shared<T>(_queue.front()));
    _queue.pop();
    return res;
}
template class threadsafe_queue<int>;